package utils

import (
	"reflect"
	"strings"

	"fmt"
	"gitee.com/dayu1985/framework/logging"
	"gitee.com/dayu1985/can_api/errors"
)

func RftStruct(obj interface{}, fn func(ty reflect.Type, vl reflect.Value, tag reflect.StructTag)) {
	ty := reflect.TypeOf(obj)
	vl := reflect.ValueOf(obj)
	if ty.Kind() == reflect.Ptr {
		ty = ty.Elem()
		vl = vl.Elem()
	}
	if ty.Kind() == reflect.Struct {
		for i := 0; i < ty.NumField(); i++ {
			t := ty.Field(i).Type
			v := vl.Field(i)
			tag := ty.Field(i).Tag
			fn(t, v, tag)
		}
	}
}

//只支持单层struct反射，也就是说，struct中不能再包含struct属性
func RftModelToView(view interface{}, model interface{}) {
	vty := reflect.TypeOf(view)
	vvl := reflect.ValueOf(view)
	if vty.Kind() != reflect.Ptr {
		panic("view 与 model 的转换，view必须为指针类型")
	}
	vty = vty.Elem()
	vvl = vvl.Elem()
	switch vty.Kind() {
	case reflect.Struct:
		RftStructView(view, model)
	case reflect.Slice:
		RftSliceView(view, model)
	default:
		panic("view 必须为struct或者slice")

	}
}

func RftStructView(view interface{}, model interface{}) {
	vty := reflect.TypeOf(view).Elem()
	vvl := reflect.ValueOf(view).Elem()

	mty := reflect.TypeOf(model)
	mvl := reflect.ValueOf(model)
	if mty.Kind() == reflect.Ptr {
		mty = mty.Elem()
		mvl = mvl.Elem()
	}

	for i := 0; i < vty.NumField(); i++ {
		vt := vty.Field(i).Type
		vv := vvl.Field(i)
		fieldname := vty.Field(i).Name
		mv := mvl.FieldByName(fieldname)
		mts, ok := mty.FieldByName(fieldname)
		if !ok {
			continue
		}
		mt := mts.Type
		//if mt == nil {
		//
		//}
		if vv.CanSet() && vt.Kind() == mt.Kind() {
			vv.Set(mv)
		} else if (vt.Kind() == reflect.Ptr) && (vt.Elem().Kind() == mt.Kind() && vv.Elem().CanSet()) {
			vv.Elem().Set(mv)
		} else if (mt.Kind() == reflect.Ptr && mv.Pointer() != 0) && (vt.Kind() == mt.Elem().Kind() && vv.CanSet()) {
			vv.Set(mv.Elem())
		}
	}
}

func RftSliceView(view interface{}, model interface{}) {

}

func RftMapToObj(source map[string]interface{}, obj interface{}) error {
	ty := reflect.TypeOf(obj)
	vl := reflect.ValueOf(obj)

	if ty.Kind() != reflect.Ptr || ty.Elem().Kind() != reflect.Struct || reflect.TypeOf(source).Kind() != reflect.Map {
		logging.Error("反射出错，source必须为map类型，obj必须为ptr或者struct类型", errors.RftTypeErr())
		return errors.RftTypeErr()
	}
	ty = ty.Elem()
	vl = vl.Elem()
	for i := 0; i < ty.NumField(); i++ {
		tg := ty.Field(i).Tag
		v := vl.Field(i)
		if v.Kind() == reflect.Ptr {
			v = v.Elem()
		}
		key := tg.Get("bson")
		if key == "" {
			key = tg.Get("json")
		}
		if vv, ok := source[key]; ok {
			v.Set(reflect.ValueOf(vv))
		}
	}
	return nil
}

//给一个tag值，返回这个tag值对应的属性的值
func RftFieldValueByTag(tag string, obj interface{}) interface{} {
	tag = strings.Split(tag, ",")[0]

	vty := reflect.TypeOf(obj)
	vvl := reflect.ValueOf(obj)
	if vty.Kind() == reflect.Ptr {
		vty = vty.Elem()
		vvl = vvl.Elem()
	}
	if vty.Kind() != reflect.Struct {
		panic("对象必须为struct类型")
	}
	for i := 0; i < vty.NumField(); i++ {
		vv := vvl.Field(i)
		vtag := vty.Field(i).Tag
		vt := vty.Field(i).Type

		vtagstr := vtag.Get("bson")
		if vtagstr == "" {
			vtagstr = vtag.Get("json")
		}
		vtagstr = strings.Split(vtagstr, ",")[0]
		if tag == vtagstr {
			if vv.Pointer() == 0 {
				if vt.Kind() == reflect.Ptr {
					vt = vt.Elem()
				}
				return reflect.New(vt).Interface()
			}
			return vv.Interface()
		}
	}
	return nil
}

func If(condition bool, trueVal, falseVal interface{}) interface{} {
	if condition {
		return trueVal
	}
	return falseVal
}

func Contains(obj interface{}, target interface{}) (bool, error) {
	targetValue := reflect.ValueOf(target)
	switch reflect.TypeOf(target).Kind() {
	case reflect.Slice, reflect.Array:
		for i := 0; i < targetValue.Len(); i++ {
			if targetValue.Index(i).Interface() == obj {
				return true, nil
			}
		}
	case reflect.Map:
		if targetValue.MapIndex(reflect.ValueOf(obj)).IsValid() {
			return true, nil
		}
	}
	return false, errors.ParamNotInPtrErr("not in")
}

/*
    将输入的date按照指定的partten格式化
    @version 1.0 目前只支持正常的格式，其余变态格式暂不支持
        如：yyyy,yyyy-MM,yyyy-MM-dd,yyyy-MM-dd HH,yyyy-MM-dd HH:mm,yyyy-MM-dd HH:mm:ss
    @date string 要被格式化的日期
    @partten stirng 指定格式
    @author wangdy
    return 返回格式化后的日期
*/
func timFormat(date, partten string) string {
	var ptnLength = len(partten)
	var dateLength = len(date)
	returnStr := date
	switch partten {
	case partten:
		if ptnLength <= dateLength {
			return Substr(date, 0, ptnLength)
		} else {
			var chaLen = ptnLength - dateLength
			if dateLength == 4 {
				if chaLen == 3 {
					returnStr += "-01"
					return returnStr
				} else if chaLen == 6 {
					returnStr += "-01-01"
					return returnStr
				} else if chaLen == 9 {
					returnStr += "-01-01 00"
					return returnStr
				} else if chaLen == 12 {
					returnStr += "-01-01 00:00"
					return returnStr
				} else if chaLen == 15 {
					returnStr += "-01-01 00:00:00"
					return returnStr
				}
			} else if dateLength == 7 {
				if chaLen == 3 {
					returnStr += "-01"
					return returnStr
				} else if chaLen == 6 {
					returnStr += "-01 00"
					return returnStr
				} else if chaLen == 9 {
					returnStr += "-01 00:00"
					return returnStr
				} else if chaLen == 12 {
					returnStr += "-01 00:00:00"
					return returnStr
				}
			} else if dateLength == 10 {
				if chaLen == 3 {
					returnStr += " 00"
					return returnStr
				} else if chaLen == 6 {
					returnStr += " 00:00"
					return returnStr
				} else if chaLen == 9 {
					returnStr += " 00:00:00"
					return returnStr
				}
			} else if dateLength == 13 {
				if chaLen == 3 {
					returnStr += ":00"
					return returnStr
				} else if chaLen == 6 {
					returnStr += ":00:00"
					return returnStr
				}
			} else if dateLength == 16 && chaLen == 3 {
				returnStr += ":00"
				return returnStr
			}
			return "xxxxxxxxxx" + date
		}
	default:
		fmt.Println("it's default case!")
		return date
	}
}

/*
    按照格式截取字符串
    @str string 要被截取的源字符串
    @start int 开始索引，从0开始
    @length int 要截取的长度
    @author wangdy
    return 返回截取后字符串
*/
func Substr(str string, start, length int) string {
	rs := []rune(str)
	rl := len(rs)
	end := 0

	if start < 0 {
		start = rl - 1 + start
	}
	end = start + length

	if start > end {
		start, end = end, start
	}

	if start < 0 {
		start = 0
	}
	if start > rl {
		start = rl
	}
	if end < 0 {
		end = 0
	}
	if end > rl {
		end = rl
	}
	return string(rs[start:end])
}
